#pragma once

#ifndef NULL
#   undef  NULL
#	define NULL	0
#endif

#ifndef __SAFE_FREE
#	define __SAFE_FREE(p)	if( p != NULL ){ delete p;p = NULL;}
#	define __SAFE_FREES(p)	if( p != NULL ){ delete[] p;p = NULL;}
#endif

template<class T>
class Array
{
public:
	Array(void)
	{
		m_pBegin = NULL;
		m_pLast = NULL;
		m_pEnd = NULL;
	}

	~Array(void)
	{
		_Clear();
	}

	size_t		size()const				{ return m_pLast - m_pBegin;}
	bool		empty()const			{ return m_pLast == m_pBegin;}
	size_t		buffer_size()const		{ return m_pEnd - m_pBegin;}
	T		*	begin()const			{ return m_pBegin;}
	T		*	end()const				{ return m_pLast;}
	T		&	front()					{ return *m_pBegin; }
	T		&	back()					{ return *(m_pLast-1);	}
	void		reserve(size_t nSize)	{ T *lastptr = _Reserve(nSize);__SAFE_FREES(lastptr); }
	
	void		resize(size_t nSize)
	{
		T *lastptr = _Reserve( nSize );
		m_pLast = m_pBegin + nSize;
		memset( m_pBegin,0,sizeof(T) * buffer_size() );
		__SAFE_FREES(lastptr);
	}

	void		insert(size_t pos,const T& value)
	{
		const T * pv = &value;
		insert(pos,pv,pv + 1);
	}

	void		insert(size_t pos,const T* pBegin,const T* pEnd)
	{
		T *lastptr = NULL;
		size_t nAddLen = pEnd - pBegin;
		if( nAddLen == 0 )
			return;
		if( size() + nAddLen >= buffer_size() )
			lastptr = _Reserve( size() + nAddLen );
		size_t nSize = size();
		if( pos > nSize )
		{
			pos = nSize > 0 ? nSize: 0;			
		}
		
		if( nSize == pos )
		{
			memmove(m_pLast,pBegin,nAddLen * sizeof(T));
		}
		else
		{
			size_t nMove = nSize - pos;// buffer_size() - pos - 1 - nAddLen;
			memmove( m_pBegin + pos + nAddLen,m_pBegin + pos,nMove * sizeof(T) );
			memmove(m_pBegin + pos,pBegin,nAddLen * sizeof(T));
		}
		
		m_pLast += nAddLen;
		__SAFE_FREES(lastptr);
	}

	void		erase(size_t pos)
	{
		erase(pos,1);
	}

	void		erase(size_t pos,size_t count)
	{
		size_t nSize = size();
		if( pos >= nSize )
			return;

		count = count + pos > nSize ? nSize - pos - 1: count;
		size_t nMoveLen = nSize - pos - count;
		memmove(m_pBegin + pos,m_pBegin + pos + count,nMoveLen * sizeof(T));
		m_pLast -= count;
		memset(m_pLast,0,(buffer_size() - size()) * sizeof(T));
	}

	void		push_back(const T&value)
	{
		insert(size(),value);
	}

	void		push_front(const T&value)
	{
		insert(0,value);
	}

	void		pop_front()
	{
		erase(0,1);
	}

	void		pop_back()
	{
		erase(size()-1,1);
	}

	void		clear()
	{
		if( size() == 0 )
			return;
		memset( m_pBegin,0,sizeof(T) * buffer_size() );
		m_pEnd = m_pLast = m_pBegin;
	}

	size_t		count_of(const T&value)
	{
		if( empty() )
			return 0;
		size_t count = 0;
		for(T *p = m_pBegin;p < m_pLast;p++)
		{
			if( *p == value )
				count ++;
		}
		return count;
	}

	bool		exist(const T&value)
	{
		return indexof(value) != -1;
	}

	size_t		indexof(const T&value)
	{
		if( empty() )
			return -1;
		for(T *p = m_pBegin;p < m_pLast;p++)
		{
			if( *p == value )
				return p - m_pBegin;
		}
		return -1;

	}

	size_t		remove(const T&value,size_t count = 1)
	{
		size_t s = size();
		if( s == 0 )
			return 0;
		size_t rsize = 0;
		for( size_t i = s - 1;i >= 0;i--)
		{
			if( value == m_pBegin[i] )
			{
				erase(i);
				rsize ++;
				if( rsize == count )
					return rsize;
			}
		}
		return rsize;
	}

	operator T*()const		{ return m_pBegin;}
	operator T*()			{ return m_pBegin;}
	T	&	get(int i)		{ return m_pBegin[i];}

protected:
	void		_Clear()
	{
		__SAFE_FREES(m_pBegin);
	}

	T	*	_Reserve(size_t pSize)
	{
		T *lastptr = NULL;
		size_t nLen = size();
		size_t nMaxLen = buffer_size();
		if(nMaxLen <= pSize)
		{
			pSize = _AssignSize( nMaxLen,pSize );
			T	* pNew = new T[pSize];
			memset(pNew,0,sizeof(T) * pSize);
			if( m_pBegin != NULL )
			{
				memcpy( pNew,m_pBegin,sizeof(T) * nLen );
				//_Clear();
				lastptr = m_pBegin;
				
			}

			m_pBegin = pNew;
			m_pLast = m_pBegin + nLen;
			m_pEnd = m_pBegin + pSize;	
		}
		else
		{
			if( pSize < nLen )
				m_pLast = m_pBegin + pSize;
			memset(m_pLast,0,(buffer_size() - size()) * sizeof(T));
		}
		return lastptr;
	}
	
	size_t	_AssignSize(size_t nLastSize,size_t nNeedSize)
	{
		nLastSize = nLastSize == 0 ? 4 : nLastSize;
		while( nLastSize <= nNeedSize )
			nLastSize *= 2;
		return nLastSize;
	}

protected:
	T	*		m_pBegin;
	T	*		m_pLast;
	T	*		m_pEnd;
};
